import socket
# import time
import sched
from threading import Thread
# from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
# from apscheduler.schedulers.blocking import BlockingScheduler
from struct import pack,unpack
from config import Config
url = Config.url
port = Config.port

# import logging
# from logging.handlers import RotatingFileHandler
import time
from Unpack_Content import Unpack_Content
unpack_content = Unpack_Content()

# fh = RotatingFileHandler(filename='ATS.log',encoding='utf-8',maxBytes=3000*1024,backupCount=2)
# logging.basicConfig(
#             format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
#             datefmt='%Y-%m-%d %H:%M:%S',
#             handlers=[fh, ],
#             level=logging.DEBUG
#         )

def Complement(num,cont):
    num = num*2
    cont = cont.split('x')[-1]
    if len(cont) < num:
        cont = '0' * (num-len(cont)) + cont
    cont = '0x' + cont
    return cont


month = hex(datetime.now().month)
month = Complement(1, month)
day = hex(datetime.now().day)
day = Complement(1, day)

so = socket.socket()
so.bind((url, port))
so.listen()
conn, addr = so.accept()

# print(len('0000000010000000000001001100010'))


# print(data_head + total)
# print(hex(data_head))
# print(2*hex(types))
# print(hex(data_len))
# print(2*hex(data_head) + hex(total) + hex(index) + hex(data_len) + hex(Dev_Status) +  hex(types) + hex(content) +
#       2*hex(data_tail))
# 心跳信息
PANT = b'0xEF0xEF0x010x010x06000x010x01000xffffffff0xFD0xFD'

# 列车信息 每秒发送
train = b'0xEF0xEF0x010x010x64000x010x02000x02000x020x01000x020x01000x010x03000x09000x190000000x550x000x01FF0x010x550x550x550xFFFFFFFF0x550xAA0xFF0x020x02000x020x02000x020x03000x09000x190000000x550x000x01FF0x010x550x550x550xFFFFFFFF0x550xAA0xFF0xFD0xFD'
# 计划信息
# plan = f'0xEF0xEF0x010x010x28000x010x03000x02000x02000x010x01000x020x01000x020x01000x010xE507{当前月份}{当前日期}{时}{分}0x3b0xE507{月}{日}{时}{分}{秒}0x0C000x010x010x000x010x03000x000x000x01000x020x01000x020x02000x020x02000x010xE507{当前月份}{当前日期}{时}{分}0x3b0xE507{月}{日}{时}{分}{秒}0x01000x010x010x000x010x01000x000x000xFD0xFD'
# 首末班信息
first_last_train = f'0xEF0xEF0x010x010x64000x010x04000x02000x02000x010x020x01000x020x01000x000xE507{month}{day}0x060x0F0x000xE507{month}{day}0x060x0F0x1E0x0C000x020x02000x020x02000x000xE507{month}{day}0x170x1E0x000xE507{month}{day}0x170x1E0x1E0x0C000x02000x020x020x02000x020x02000x000xE507{month}{day}0x060x0F0x000xE507{month}{day}0x060x0F0x1E0x01000x020x02000x020x02000x000xE507{month}{day}0x170x1E0x000xE507{month}{day}0x170x1E0x1E0x0C000xFD0xFD'
# 车门隔离状态信息
Door_isolation_status = b'0xEF0xEF0x010x010x1E000x010x05000x000000010x000000020x00000000000000010X000000030x00000000000000030xFD0xFD'
# 站台门隔离状态信息 每10秒发送一次
Platform_door = b'0xEF0xEF0x010x010x5B000x010x06000x020x000000010x000000010x00000000000000030x00000000000000060x000000040x00000000000000070x00000000000000090x000000020x000000020x00000000000000040x00000000000000080x000000060x00000000000000090x000000000000000E0xFD0xFD'
# 站场表示信息 每五秒发送一次
# station_yard = b'0xEF0xEF0x010x010x12000x010x07000x010x010x640000000x0A000x110x040x620242000xFD0xFD'

funs = list()

# 底层帧格式清单
basement_frame = {
    'data_head': 2, # data_head为帧头，两个字节，固定为0xEF,0xEF
    'total': 1, # Total:总帧数（1-32），最大定义32个分包
    'index': 1, # 前帧（1-32），对于长度超过后面data限制的应用数据，需要分包发送和接收，只有在收到index=total时，将前面的帧的data合并在一起进行处理；
    'data_len': 2,# data_len 表示后接的应用数据（Type及Content）的长度,两个字节，低字节在前，高字节在后；
    'Dev_Status': 1, # 表示设备当前主/备机状态，0x01:当前为主机，0x02:当前为备机；
    'Type': 2, # Type：消息种类，2BYTE，低位在前
    'Content': 'n',# Content: 具体消息内容，根据不同消息种类不同而不同
    'data_tail': 2 # data_tail为帧尾，两个字节，固定为0xFD,0xFD。
}

# 供电臂信息对应清单
supply_electricity = {
    '供电区段数目': 2,
    '供电区段编号': 2,
    '供电区段状态': 1
}
# 记录每次获得数据的时间，没收到数据后每10秒发送一次心跳，超过30秒重新连接
GetData_Time = int(time.time())
# msg_list = [train, Door_isolation_status,Platform_door,station_yard]

# 用于判定是否接收到客户端心跳的变量
ISCS_PANT = False
# 判断是否发送服务端心跳
ATS_PANT = False
# reconnect = False
# def send_PANT(msg):
#
#     length = pack('i', len(msg))
#     conn.send(length)
#     conn.send(msg)


# 发送信息到客户端的函数
def send_msg(msg):
    print(msg)
    global conn, addr, ISCS_PANT, ATS_PANT
    # 将表示数据长度的数据压缩成4个字节
    length = pack('i',len(msg))
    try:
        # 首先发送数据长度
        conn.send(length)
        # 发送数据
        conn.send(msg)
    except Exception as e:
        # 断开已有连接
        try:
            conn.close()
            # 重新建立连接
            so = socket.socket()
            so.bind((url, port))
            so.listen()
            conn, addr = so.accept()
            ATS_PANT = False
            # ISCS_PANT = False
        except:
            pass
    #     try:
    #         so=socket.socket()
    #         so.bind(('127.0.0.1', 10020))
    #         so.listen()
    #         conn, addr=so.accept()
    #         conn.send(PANT)
    #         ATS_PANT = True
    #     except:
    #         pass
    # print(msg.split('/'))


def send_first_last_train():
    month=hex(datetime.now().month)
    month=Complement(1, month)
    day=hex(datetime.now().day)
    day=Complement(1, day)
    first_last_train=f'0xEF0xEF0x010x010x64000x010x04000x02000x02000x010x020x01000x020x01000x000xE507{month}{day}0x060x0F0x000xE507{month}{day}0x060x0F0x1E0x0C000x020x02000x020x02000x000xE507{month}{day}0x170x1E0x000xE507{month}{day}0x170x1E0x1E0x0C000x02000x020x020x02000x020x02000x000xE507{month}{day}0x060x0F0x000xE507{month}{day}0x060x0F0x1E0x01000x020x02000x020x02000x000xE507{month}{day}0x170x1E0x000xE507{month}{day}0x170x1E0x1E0x0C000xFD0xFD'
    first_last_train = bytes(first_last_train.encode('utf-8'))
    # length =pack('i',len(first_last_train))
    send_msg(first_last_train)

def send_plan():
    # print('plan')
    global conn, addr, ATS_PANT, ISCS_PANT, reconnect
    date = datetime.now()
    # 获取当前时间月份
    month = hex(date.month)
    month = Complement(1, month)
    # 获取当前时间日期
    day = hex(date.day)
    day = Complement(1, day)
    # 小时
    hour = hex(date.hour)
    hour = Complement(1, hour)
    # 分
    minute = hex(date.minute)
    minute = Complement(1, minute)
    second = hex(date.second)
    second = Complement(1, second)
    # 设定45秒后离站
    t = time.time()+45
    timeArray = time.localtime(t)
    # 离站时月份
    leave_month = hex(timeArray.tm_mon)
    leave_month = Complement(1, leave_month)
    # 离站时day
    leave_day = hex(timeArray.tm_mday)
    leave_day = Complement(1, leave_day)
    # 离站时小时
    leave_hour = hex(timeArray.tm_hour)
    leave_hour = Complement(1, leave_hour)
    # 离站时分钟
    leave_min = hex(timeArray.tm_min)
    leave_min = Complement(1, leave_min)
    # 离站秒
    leave_second = hex(timeArray.tm_sec)
    leave_second = Complement(1, leave_second)


    # 计划信息
    plan=f'0xEF0xEF0x010x010x4C000x010x03000x02000x02000x010x01000x020x01000x020x01000x000xE507{month}{day}{hour}{minute}{second}0xE507{leave_month}{leave_day}{leave_hour}{leave_min}{leave_second}0x0C000x010x010x000x010x03000x000x000x02000x020x01000x020x02000x020x02000x000xE507{month}{day}{hour}{minute}0x3b0xE507{leave_month}{leave_day}{leave_hour}{leave_min}{leave_second}0x01000x010x010x000x010x01000x000x000xFD0xFD'
    # 变成bytes类型
    plan = bytes(plan.encode('utf-8'))
    # 数据长度
    length=pack('i', len(plan))
    try:
        # 发送数据长度
        conn.send(length)
        # 发送计划信息
        conn.send(plan)
    except Exception as e:
        raise e
        # reconnect = True
    # try:
    #     conn.send(length)
    #     conn.send(plan)
    # except:
    #     ATS_PANT = False
    #     ISCS_PANT = False
    #     try:
    #         so=socket.socket()
    #         so.bind(('127.0.0.1', 10020))
    #         so.listen()
    #         conn, addr=so.accept()
    #         conn.send(PANT)
    #         ATS_PANT = True
    #     except:
    #         pass


# 将接受到的数据转化成10进制
def inversion(data):
    # data = data #type:str
    # 先将bytes数据转换成str
    data = str(data,encoding='utf-8')
    # 切割
    val = data.split('x')[-1]
    val_list = list()
    for i in range(0,len(val),2):
        val_list.append(val[i:i+2:1])
    new_val_list = val_list[::-1]
    new_data = ''.join(new_val_list)
    new_data = '0x' + new_data
    return int(new_data, 16)


# 将数据从低位在前变为高位在前
def reversal(data):
    # data = data #type:str
    # 先将bytes数据转换成str
    if isinstance(data,bytes):
        data=str(data, encoding='utf-8')
    # 切割
    val=data.split('x')[-1]
    val_list=list()
    for i in range(0, len(val), 2):
        val_list.append(val[i:i + 2:1])
    new_val_list=val_list[::-1]
    new_data=''.join(new_val_list)
    new_data='0x' + new_data
    return new_data


def send_platform_door():
    global Platform_door
    scheduler = sched.scheduler(time.time, time.sleep)
    # if ISCS_PANT:
    while True:
        if ISCS_PANT:
            scheduler.enter(10, 0, send_msg, argument=(Platform_door,))
            scheduler.run()


def send_train():
    global train
    scheduler = sched.scheduler(time.time, time.sleep)
    # if ISCS_PANT:
    while True:
        # print('train',train)
        if ISCS_PANT:
            scheduler.enter(1, 0, send_msg, argument=(train,))
            scheduler.run()


# 设备编码从2201开始
occupy_num = 2201
switch_occupy_num = 2221


# 模拟发送站场表示信息，模拟发送20个逻辑区段
def send_yard():
    scheduler = sched.scheduler(time.time,time.sleep)
    global ISCS_PANT, occupy_num, switch_occupy_num
    # 表示逻辑区段CBTC通信列车占用状态为1
    occupy='20000000'
    # 表示逻辑区段CBTC通信列车占用状态为0
    no_occupy='00000000'
    # 表示道岔CBTC通信列车占用状态为1
    switch_occupy='0001000000'
    # 表示道岔CBTC通信列车占用状态为0
    switch_no_occupy='0000000000'
    while True:
        # print(ISCS_PANT)
        # 如果已经接收到心跳信息
        # if ISCS_PANT == True:
        #     for msg in msg_list:
        #         if msg == Platform_door:
        #             # 每隔10秒发送站台门隔离状态信息
        #             scheduler.enter(10, 0, send_msg, argument=(msg,))
        #
        #         if msg == station:
        #             # 每隔一秒发送列车信息
        #             scheduler.enter(1, 0, send_msg, argument=(msg,))
            if ISCS_PANT:
                station_yard_content=''
                # 模拟20个逻辑区段
                for i in range(2201, 2221):
                    # 设备编码变成16进制
                    eq_code = hex(i)
                    # 设备编码4个字节表示，即8个16进制位，如果不够则补全
                    if len(eq_code) < 2+8:
                        code = eq_code.split('x')[-1]
                        # 不足高位补0
                        eq_code ='0x' + '0'*(8-len(code)) + code
                    eq_code = reversal(eq_code)
                    # CBTC通信列车占用状态顺序变化占用，
                    if i == occupy_num:
                        cupy = occupy
                    else:
                        cupy = no_occupy
                    station_yard_content = station_yard_content + f'0x11{eq_code}0x040x{cupy}'
                for i in range(2221, 2225):
                    eq_code=hex(i)
                    if len(eq_code) < 2 + 8:
                        code=eq_code.split('x')[-1]
                        eq_code='0x' + '0' * (8 - len(code)) + code
                    eq_code=reversal(eq_code)
                    if i == switch_occupy_num:
                        switch_cupy=switch_occupy
                    else:
                        switch_cupy=switch_no_occupy

                    station_yard_content=station_yard_content + f'0x14{eq_code}0x050x{switch_cupy}'
                occupy_num += 1
                if occupy_num == 2221:
                    occupy_num=2201
                switch_occupy_num += 1
                if switch_occupy_num == 2225:
                    switch_occupy_num=2221
                # 拼接站场表示信息
                station_yard = f'0xEF0xEF0x010x010xfc000x010x07000x010x010x160000000xf400{station_yard_content}0xFD0xFD'.encode('utf-8')
                station_yard = bytes(station_yard)
                # print(station_yard)
                scheduler.enter(5, 0, send_msg, argument=(station_yard,))
                scheduler.run()


# 发送服务端心跳
def sen_PANT():
    scheduler = sched.scheduler(time.time, time.sleep)
    # while True:
    global ATS_PANT, ISCS_PANT, conn, addr, GetData_Time
    # print(ATS_PANT,ISCS_PANT)
    # if ISCS_PANT and ATS_PANT == False:
        # print('send')
    # 如果已经接收到客户端信息信息以及服务端未发送心跳信息
    while True:
        Time = int(time.time())
        # print(Time)
        # 如果服务端没有发送过心跳
        if not ATS_PANT:
            # 发送心跳信息
            scheduler.enter(0, 0, send_msg, argument=(PANT,))
            scheduler.run()
            ATS_PANT= True
        # 如果没接收到信息的时长超过10秒但是没有超过30秒，重新发送心跳
        if Time - GetData_Time >= 10 and Time - GetData_Time < 30:
            scheduler.enter(0, 1, send_msg, argument=(PANT,))
            ATS_PANT = True
        # 如果没有接收到信息的时长超过30秒,重新建立连接
        elif Time - GetData_Time >= 30:
            # 断开已有连接
            conn.close()
            # 重新建立连接
            so = socket.socket()
            so.bind((url, port))
            so.listen()
            conn, addr = so.accept()
            # 重新建立连接后重新发送心跳
            scheduler.enter(0, 1, send_msg, argument=(PANT,))
            # 表示已经发送过心跳
            # ISCS_send_PANT = True
            # 表示服务端已经想客户端发送心跳
            ATS_PANT = True


def run_send_plan():
    # 如果已经接收到客户端心跳信息
    # if ISCS_PANT:
        # scheduler = BlockingScheduler()
        # # 每分钟的45秒发送计划信息
        # scheduler.add_job(send_plan, 'cron', month='1-12',day='1-31', hour='0-23',minute='0-59',second=45)
        # scheduler.start()
    scheduler = sched.scheduler(time.time, time.sleep)
    while True:
        global ISCS_PANT
        if ISCS_PANT:
            scheduler.enter(5, 0, send_plan)
            scheduler.run()

def send_door_isolation_status():
    # print(1,ISCS_PANT)
    # print(1)
    # if ISCS_PANT:
        # print('车门')
        # scheduler=BlockingScheduler()
        # # 每分钟的45秒发送车门隔离状态信息
        # scheduler.add_job(send_msg, 'cron', month='1-12', day='1-31', hour='0-23', minute='0-59', second=45, args=(Door_isolation_status, ))
        # scheduler.start()
    scheduler = sched.scheduler(time.time, time.sleep)
    while True:
        global ISCS_PANT
        if ISCS_PANT:
            scheduler.enter(5, 0, send_msg, argument=(Door_isolation_status, ))
            scheduler.run()
# 将对应值变成高位在前地位在后，并返回10进制数



# 解析数据的主要内容，并反回结果
# def unpack_content(Type, content):
#     global gdb_qd
#     # 如果类型为8，说明是供电臂信息内容
#     if Type == 8:
#         one_con_len = 0
#         # 切片的位置
#         position = 0
#         # 存放多条信息的列表
#         contents = list()
#         uncontents = list()
#         # 数据的开头为信息长度，获取这个数据的长度
#         content_num_len = list(gdb_qd.values())[0]
#         # content_num = inversion(content_num)
#         content_num = content[0:content_num_len*2+2:1]
#         # 获取信息长度
#         content_num = inversion(content_num)
#         print('content_num:', content_num)
#         # print(content_num_len*2+2)
#         content = content[content_num_len*2+2::1]
#         # print('content',content)
#         # new_gdb_qd = dict()
#         # print(list(gdb_qd.values())[1::1])
#         for value in list(gdb_qd.values())[1::]:
#             # 获取每条信息的长度
#             one_con_len += 2*value + 2
#         # print(one_con_len)
#         while position+one_con_len <= len(content):
#             # 切割出每一条信息
#             con = content[position:position+one_con_len:1]
#             # print(con)
#             # 将每个信息放到列表当中
#             contents.append(con)
#             position += one_con_len
#         print(contents)
#         # 遍历每一条信息
#         for cont in contents:
#             cont_position = 0
#             cont_dict = dict()
#             for key,lent in list(gdb_qd.items())[1::]:
#                 # print(lent)
#                 # print(cont_position)
#                 val = cont[cont_position:cont_position+((2*lent)+2):1]
#                 cont_dict[key] = inversion(val)
#                 cont_position += ((2*lent)+2)
#             uncontents.append(cont_dict)
#         return uncontents


# 获取客户端发送的数据的方法
def receive():
    global ISCS_PANT, conn, addr, ATS_PANT, GetData_Time

    while True:
        # print('sss')
        # try:
        # print('ats',ATS_PANT)
        try:
        # 首先获取数据的长度，避免黏包
            client_msg_len = conn.recv(4)
            # print(client_msg_len)
            msg_len = unpack('i', client_msg_len)[0] #type:int
            # 接收客户端发送的数据
            client_msg = conn.recv(msg_len)
            # 如果客户端发送的是首末班申请，则直接发送首末班信息给客户端
            if client_msg == b'0X00000000':
                send_first_last_train()
                GetData_Time=int(time.time())
                continue
            print(client_msg)
            # 每次取得客户端发送的信息时记录一下时间戳
            GetData_Time = int(time.time())
        # 发生错误时尝试重新建立连接
        except:
            # ATS_PANT=False
            continue

        # 数据切割的位置
        position = 0
        # 用于存放数据依据底层帧拆分后的结果
        ISCS_MSG = dict()
        # 遍历底层帧清单,做初步的数据切分
        for key, value in basement_frame.items():
            # if key == 'data_head':
            #     data_head = client_msg[]
            if value != 'n':
                # print(key)
                if key == 'data_head' or key == 'data_tail':
                    ISCS_MSG[key] = client_msg[position:position+(2*value+4):1]
                    position += 2*value+4

                else:
                    ISCS_MSG[key] = client_msg[position:position+(2*value+2):1]
                    position += 2 * value + 2

                # print(key)

            else:
                # print(key)
                # 取得主要内容部分
                content = client_msg[position:-8:1]
                ISCS_MSG['content'] = content
                # print(content)
                position += len(content)

        Type = ISCS_MSG.get('Type')
        # 如果总帧数为1
        if int(ISCS_MSG.get('total'), 16) == 1:
            # 如果数据类型为1，说明是心跳信息
            if inversion(Type) == 1:
                content = str(ISCS_MSG.get('content'), encoding='utf-8')
                # 判断心跳内容是否正确
                if content == '0xffffffff':

                    print('获取客户端心跳成功')
                    ISCS_PANT = True
                    # logging.debug(f'客户端心跳{client_msg}')
            elif inversion(Type) == 8:
                print('供电臂信息')
                # print(unpack_content(8,ISCS_MSG.get('content')))
                # logging.debug(f'供电臂{client_msg}')


receive_t = Thread(target=receive)
PANT_t = Thread(target=sen_PANT)
send_yard_t = Thread(target=send_yard)
run_send_plan_t = Thread(target=run_send_plan)
first_last_train = bytes(first_last_train.encode('utf-8'))
first_last_train_t = Thread(target=send_msg,args=(first_last_train,))
door_isolation_status_t = Thread(target=send_door_isolation_status)
send_platform_door_t = Thread(target=send_platform_door)
send_train_t = Thread(target=send_train)

send_platform_door_t.start()
receive_t .start()
send_train_t.start()
PANT_t.start()
send_yard_t.start()
run_send_plan_t.start()
first_last_train_t.start()
door_isolation_status_t.start()

receive_t .join()
PANT_t.join()
run_send_plan_t.join()
send_yard_t.join()
first_last_train_t.join()
door_isolation_status_t.join()
send_platform_door_t.join()
send_train_t.join()





